﻿using Hx.ADSyncPlatform.ActiveDirectory.Attributes;
using Hx.ADSyncPlatform.ActiveDirectory.Enums;
using Hx.ADSyncPlatform.ActiveDirectory.Model;
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;
using System.Linq;

namespace Hx.ADSyncPlatform.ActiveDirectory.Access
{
    /// <summary>
    /// 人员
    /// </summary>
    internal class PersonAccess
    {
        /// <summary>
        /// 查询单个用户
        /// </summary>
        /// <param name="rootEntry">查询的根AD对象</param>
        /// <param name="sAMAccountName">账户名</param>
        /// <returns>返回用户SearchResult</returns>
        public SearchResult Single(DirectoryEntry rootEntry, string sAMAccountName)
        {
            string filter = string.Format("(&(objectclass=user)(objectcategory=person)(sAMAccountName={0}))", sAMAccountName);
            DirectorySearcher dsearcher = new DirectorySearcher(rootEntry, filter, null, SearchScope.Subtree);
            Console.WriteLine(filter);
            //根据计算机名搜索用户
            //string f2 = "(&(objectclass=user)(objectcategory=person)(userWorkstations=*AREIS-APP*))";
            //DirectorySearcher dsearcher1 = new DirectorySearcher(rootEntry, f2, null, SearchScope.Subtree);
            //var res=dsearcher1.FindAll();
            SearchResult result = dsearcher.FindOne();
            return result;
        }
        /// <summary>
        /// 查询单个用户
        /// </summary>
        /// <param name="rootEntry">查询的根AD对象</param>
        /// <param name="sAMAccountName">账户名</param>
        /// <returns>返回用户SearchResult</returns>
        public SearchResult SingleByDistinguishedName(DirectoryEntry rootEntry, string distinguishedName)
        {
            string filter = string.Format("(&(objectclass=user)(objectcategory=person)(DistinguishedName={0}))", distinguishedName);
            DirectorySearcher dsearcher = new DirectorySearcher(rootEntry, filter, null, SearchScope.Subtree);
            SearchResult result = dsearcher.FindOne();
            return result;
        }
        /// <summary>
        /// 查询用户
        /// 显示名模糊查询
        /// </summary>
        /// <param name="rootEntry">查询的根AD对象</param>
        /// <param name="displayName">显示名</param>
        /// <returns>返回用户SearchResultCollection</returns>
        public SearchResultCollection Get(DirectoryEntry rootEntry, string displayName)
        {
            string filter = string.Format("(&(objectclass=user)(objectcategory=person)(|(displayName={0}*)(samaccountname={0}*)))", displayName);
            DirectorySearcher dsearcher = new DirectorySearcher(rootEntry, filter, null, SearchScope.Subtree);
            SearchResultCollection result = dsearcher.FindAll();
            return result;
        }

        /// <summary>
        /// 查询组织下的用户
        /// </summary>
        /// <param name="org">所在组织</param>
        /// <param name="searchScope">查询层级</param>
        /// <param name="searchLocation">查询位置</param>
        /// <returns>返回用户SearchResultCollection</returns>
        public SearchResultCollection Get(DirectoryEntry org, SearchScope searchScope, string displayName = "")
        {
            string filter = "(&(objectclass=user)(objectcategory=person))";
            if (!string.IsNullOrEmpty(displayName))
            {
                filter = string.Format("(&(objectclass=user)(objectcategory=person)(|(displayName={0}*)(samaccountname={0}*)))", displayName);
            }
            DirectorySearcher dsearcher = new DirectorySearcher(org, filter, null, searchScope);
            SearchResultCollection result = dsearcher.FindAll();
            return result;
        }

        /// <summary>
        /// 根据用户账号，查询用户信息
        /// </summary>
        /// <param name="rootEntry">查询的根AD对象</param>
        /// <param name="samAccountNames">需要查询的用户账户</param>
        /// <param name="propertiesToLoad">需要返回的参数</param>
        /// <returns>返回用户SearchResultCollection</returns>
        public SearchResultCollection Get(DirectoryEntry rootEntry, List<string> samAccountNames, List<string> propertiesToLoad)
        {
            if (samAccountNames == null || !samAccountNames.Any())
            {
                return null;
            }
            string filter = $"(|{string.Join("", samAccountNames.Select(item => $"(samaccountname={item})"))})";
            DirectorySearcher dsearcher = new DirectorySearcher(rootEntry, filter, propertiesToLoad.ToArray(), SearchScope.Subtree);
            SearchResultCollection result = dsearcher.FindAll();
            return result;
        }

        /// <summary>
        /// 自定义查询
        /// </summary>
        /// <param name="rootEntry">查询的根AD对象</param>
        /// <param name="filter">LDAP查询语句</param>
        /// <param name="propertiesToLoad">需要返回的属性</param>
        /// <param name="searchScope">查询层级</param>
        /// <param name="pageSize">最大条数</param>
        /// <returns>返回用户SearchResultCollection</returns>
        public SearchResultCollection Search(DirectoryEntry rootEntry, string filter, List<string> propertiesToLoad, SearchScope searchScope)
        {
            DirectorySearcher dsearcher = new DirectorySearcher(rootEntry, filter, propertiesToLoad?.ToArray(), searchScope);
            //if (pageSize > 0)
            //{
            //    //hack:确定分页代码可用？
            //    dsearcher.PageSize = pageSize;
            //}
            SearchResultCollection searchResult = dsearcher.FindAll();
            return searchResult;
        }

        /// <summary>
        /// 创建人员
        /// </summary>
        /// <param name="orgDe">人员创建的组织</param>
        /// <param name="per">人员信息</param>
        /// <param name="password">密码</param>
        /// <returns>成功True，失败False</returns>
        public bool Create(DirectoryEntry orgDe, Person per, string password)
        {
            if (string.IsNullOrWhiteSpace(per.CN))
                throw new ArgumentNullException($"创建用户时【CN】是必要属性，不能为空。");

            var de = orgDe.Children.Add("CN=" + per.CN, "user");
            UserAccountControlValue userAccountControlValue = UserAccountControlValue.NORMAL_ACCOUNT;
            foreach (var property in per.GetType().GetProperties())
            {
                if (per.AssignedAttributes.Contains(property.Name))
                {
                    var attributes = property.GetCustomAttributes(typeof(ReadOnlyCustomAttribute), false);
                    var mustCreate = property.GetCustomAttributes(typeof(RequiredForCreateAttribute), false);
                    var v = property.GetValue(per, null);
                    //判断必须的字段是否为空
                    if (mustCreate != null && mustCreate.Any() && v == null)
                    {
                        throw new ArgumentNullException($"创建用户时【{property.Name}】是必要属性，不能为空。");
                    }
                    if (attributes == null || attributes.Length == 0)
                    {
                        if (v != null)
                        {
                            if (property.Name?.ToLower() == "userAccountControl".ToLower())
                            {
                                userAccountControlValue = (UserAccountControlValue)v;
                                if (userAccountControlValue <= 0)
                                {
                                    userAccountControlValue = UserAccountControlValue.NORMAL_ACCOUNT;
                                }
                            }
                            else
                            {
                                if (property.Name?.ToLower() != "accountExpires".ToLower())
                                {
                                    de.Properties[property.Name].Value = v;
                                }
                            }

                        }
                    }
                }
            }
            de.CommitChanges();
            try
            {

                if (per.AccountExpires != null && per.AccountExpires != DateTime.MinValue && per.AccountExpires != DateTime.MaxValue)
                {
                    de.InvokeSet("AccountExpirationDate",
                            new object[] { new DateTime(per.AccountExpires.Year, per.AccountExpires.Month, per.AccountExpires.Day) }
                     );
                    de.CommitChanges();
                }
            }
            catch (Exception ex)
            {
                throw new ActiveDirectoryOperationException("用户创建成功，设置账号过期时间时失败。", ex);
            }
            try
            {
                //解决出现连接智能卡的问题
                de.AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.Signing | AuthenticationTypes.Sealing;
                de.Properties["userAccountControl"].Value = userAccountControlValue;
                de.Invoke("SetPassword", new object[] { password });
                de.CommitChanges();
            }
            catch (Exception ex)
            {
                //hack：异常被捕获却没有处理
                throw new ActiveDirectoryOperationException("用户创建成功，设置密码时失败。", ex);
            }
            return true;
        }

        /// <summary>
        /// 
        /// 修改
        /// </summary>
        /// <param name="perDe">原始人员</param>
        /// <param name="per">新修改的人员信息</param>
        /// <returns>成功True，失败False</returns>
        public bool Update(DirectoryEntry perDe, Person per)
        {
            foreach (var property in per.GetType().GetProperties())
            {

                if (per.AssignedAttributes.Contains(property.Name))
                {
                    var notUpdate = property.GetCustomAttributes(typeof(IgnoreUpdateAttribute), false);
                    var readOnlyCustom = property.GetCustomAttributes(typeof(ReadOnlyCustomAttribute), false);
                    var v = property.GetValue(per, null);
                    if ((notUpdate == null || notUpdate.Length == 0) && (readOnlyCustom == null || readOnlyCustom.Length == 0))
                    {
                        if (v == null)
                        {
                            perDe.Properties[property.Name].Clear();
                        }
                        else
                        {
                            if (property.Name?.ToLower() != "accountExpires".ToLower())
                            {
                                perDe.Properties[property.Name].Value = v;
                            }
                        }
                    }
                }
            }

            perDe.CommitChanges();
            try
            {
                if (per.AccountExpires != null && per.AccountExpires != DateTime.MinValue && per.AccountExpires != DateTime.MaxValue)
                {
                    perDe.InvokeSet("AccountExpirationDate",
                            new object[] { new DateTime(per.AccountExpires.Year, per.AccountExpires.Month, per.AccountExpires.Day) }
                     );
                    perDe.CommitChanges();
                }
            }
            catch (Exception ex)
            {
                throw new ActiveDirectoryOperationException("用户修改成功，设置账号过期时间时失败。", ex);
            }
            return true;
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="perDe">需要删除的人员信息</param>
        /// <returns>成功True，失败False</returns>
        public bool Delete(DirectoryEntry perDe)
        {
            perDe.DeleteTree();
            return true;
        }

        /// <summary>
        /// 移动
        /// </summary>
        /// <param name="orgDe">移动的目标组织</param>
        /// <param name="perDe">需要移动的人员</param>
        /// <returns>成功True，失败False</returns>
        public bool Move(DirectoryEntry orgDe, DirectoryEntry perDe)
        {
            perDe.MoveTo(orgDe);
            return true;
        }

        /// <summary>
        /// 重置密码
        /// </summary>
        /// <param name="perDe">需要重置密码的人员</param>
        /// <param name="password">新密码</param>
        /// <returns>成功True，失败False</returns>
        public bool ResetPassword(DirectoryEntry perDe, string password)
        {
            var parm = new object[] { password };
            perDe.AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.Signing | AuthenticationTypes.Sealing;
            perDe.Invoke("SetPassword", parm);
            perDe.CommitChanges();
            return true;
        }

        /// <summary>
        /// 启用/禁用账户
        /// </summary>
        /// <param name="perDe">人员</param>
        /// <param name="enable">True启用，False禁用</param>
        /// <returns>成功True，失败False</returns>
        public bool Enable(DirectoryEntry perDe, bool enable)
        {
            if (enable)
                perDe.Properties["userAccountControl"].Value =
                   (Convert.ToInt32(perDe.Properties["userAccountControl"].Value) | (int)UserAccountControlValue.ACCOUNTDISABLE) -
                   (int)UserAccountControlValue.ACCOUNTDISABLE;
            else
                perDe.Properties["userAccountControl"].Value =
                   Convert.ToInt32(perDe.Properties["userAccountControl"].Value) | (int)UserAccountControlValue.ACCOUNTDISABLE;
            perDe.CommitChanges();
            return true;
        }
    }
}
